package dev2dev.textclient.voip;

/**
 * @author jinbing.yao
 * @createTime 2023-10-19 18:14
 **/


import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import dev2dev.textclient.conv.AudioConverter;
import dev2dev.textclient.ByteUtils;
import dev2dev.textclient.conv.G711AUtil;
import dev2dev.textclient.conv.G711Converter;

import javax.sip.message.Request;
import javax.sound.sampled.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class RTCRTPHeaderExample {
    //中继器 repeater IP
    public String IP = "127.0.0.1";
    //中继器 端口 Repeater Port
    public int PORT = 5060;
    // 缓冲区大小 buffer size
//    private static final int BUFFER_SIZE = 1024;
    //每个RTP数据包中有4帧20ms音频
    private static final int BUFFER_SIZE = 4096;
    // 采样率 audio sample rate
    private static final int SAMPLE_RATE = 8000;

    boolean isStop = false;
    //采样率
    private static float RATE = 44100f;
    //编码格式PCM
    private static AudioFormat.Encoding ENCODING = AudioFormat.Encoding.PCM_SIGNED;
    //帧大小 16
    private static int SAMPLE_SIZE = 16;
    //是否大端
    private static boolean BIG_ENDIAN = false;//true
    //通道数
    private static int CHANNELS = 2;


    private Thread thread;
    public TargetDataLine targetDataLine;


    //收音解析
    public static SourceDataLine sourceDataLine;
    //1准备发送 2发送中 0停止发送
    public int audioSendSta;

    //缓存请求内容
    public Request request;

    // 创建UDP Socket
    public  DatagramSocket socket;


    public void initiateAudio(int port, int to, int fromId) {
        PORT = port;
        System.out.println("会话建立：" + to + ">>" + to);
        setAudioSendSta(2); //发送中状态

        String aisReach = request.getHeader("Ais-Reach").toString().split(":")[1].trim();

        thread = new Thread(() -> {
            try {
                socket = new DatagramSocket();
                Date data = new Date();
                AudioFormat pcmFormat = new AudioFormat(16000, 16, 1, true, false);

//                Mixer.Info[] mixers = AudioSystem.getMixerInfo();
//                Mixer mixer = AudioSystem.getMixer(mixers[0]); // 选择第一个音频设备
//                mixer.open();
//
//                Line.Info[] lineInfos = mixer.getSourceLineInfo(); // 获取音频设备的Line.Info对象
//                SourceDataLine sourceDataLine = (SourceDataLine) mixer.getLine(lineInfos[0]); // 选择第一个音频设备
//                AudioFormat pcmFormat = sourceDataLine.getFormat(); // 获取当前音频格式

                System.out.println("audioFormat.getSampleRate()"+pcmFormat.getSampleRate());
                System.out.println("audioFormat.getChannels()"+pcmFormat.getChannels());
                System.out.println("audioFormat.getSampleSizeInBits()"+pcmFormat.getSampleSizeInBits());
                System.out.println("audioFormat.getEncoding()"+pcmFormat.getEncoding());
                System.out.println("audioFormat.getFrameSize()"+pcmFormat.getFrameSize());
                //获取音频输入设备
                targetDataLine = AudioSystem.getTargetDataLine(pcmFormat);

                targetDataLine.open(pcmFormat);
                targetDataLine.start();
                AudioInputStream audioInputStream = new AudioInputStream(targetDataLine);
                // 发送PCMA数据包示例
                byte[] buffer = new byte[160*4]; // 20ms的音频数据，每个样本占用一个字节 转A率时,发送长度 字节/4

                // 读取音频数据并发送
                int version2 = 3840;
//                    int version3 = RandomUtil.randomInt(10000,20000);
                int seq = 0;
                while (getAudioSendSta() == 2) {
                    seq++;
                    audioInputStream.read(buffer, 0, buffer.length);

                    // 创建RTP固定头部
                    byte[] rtpHeader = createRTPHeader(seq, 0, 1, seq*20);
                    // 创建RTC头部
//                        byte[] rtcHeader = createRTCHeader(2, SAMPLE_RATE, 1, buffer.length, rtpHeader);
                    byte[] rtcHeader = createRTCHeaderInfo(to, fromId, "group".equals(aisReach), version2, seq, rtpHeader);
                    if(seq<4){
                        Thread.sleep(20);
                    }
                    // 发送RTC头部和RTP固定头部
//                    sendRTCHeaderAndRTPHeader(rtcHeader, rtpHeader, convertPCMtoG711a(buffer,pcmFormat));
//                    sendRTCHeaderAndRTPHeader(rtcHeader, rtpHeader, AudioConverter.convertPcmToAlaw(buffer));
//                    sendRTCHeaderAndRTPHeader(rtcHeader, rtpHeader, G711Converter.encode(buffer,0,buffer.length));
                    sendRTCHeaderAndRTPHeader(rtcHeader, rtpHeader, G711AUtil.pcmToG711A(buffer));
//                    sendRTCHeaderAndRTPHeader(rtcHeader, rtpHeader, buffer);
//                    sourceDataLine.write(buffer,0,buffer.length);
//                    System.out.println("发送包：" + seq+",长度："+buffer.length);

//                    if(DateUtil.between(new Date(),data, DateUnit.MS)>20){
//                        seq++;
//                        audioInputStream.read(buffer, 0, buffer.length);
//                        // 创建RTP固定头部
//                        byte[] rtpHeader2 = createRTPHeader(seq, 0, 1, seq*20);
//                        // 创建RTC头部
////                        byte[] rtcHeader = createRTCHeader(2, SAMPLE_RATE, 1, buffer.length, rtpHeader);
//                        byte[] rtcHeader2 = createRTCHeaderInfo(to, fromId, "group".equals(aisReach), version2, 0, rtpHeader2);
//                        // 发送RTC头部和RTP固定头部
//                        sendRTCHeaderAndRTPHeader(rtcHeader2, rtpHeader2, G711AUtil.pcmToG711A(buffer));
//                        if(seq<4){Thread.sleep(20);}
//                    }
                }
                seq++;
                audioInputStream.read(buffer, 0, buffer.length);
                // 创建RTP固定头部
                byte[] rtpHeader = createRTPHeader(seq, 0, 1, seq*20);
                // 创建RTC头部
//                        byte[] rtcHeader = createRTCHeader(2, SAMPLE_RATE, 1, buffer.length, rtpHeader);
                byte[] rtcHeader = createRTCHeaderInfo(to, fromId, "group".equals(aisReach), version2, 0, rtpHeader);
                // 发送RTC头部和RTP固定头部
                sendRTCHeaderAndRTPHeader(rtcHeader, rtpHeader, G711AUtil.pcmToG711A(buffer));
//                sendRTCHeaderAndRTPHeader(rtcHeader, rtpHeader, buffer);

                targetDataLine.stop();
                targetDataLine.close();
                // 关闭Socket
                socket.close();
            } catch (LineUnavailableException | IOException | InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
    }


    /**
     * 固定头部
     *
     * @param sequenceNumber
     * @param payloadType    08 媒体类型
     * @param marker
     * @param timestamp
     * @return
     */

    public static byte[] createRTPHeader(int sequenceNumber, int payloadType, int marker, long timestamp) {

        // 创建RTP固定头部字节缓冲区
//        ByteBuffer buffer = ByteBuffer.allocate(12);
        ByteBuffer buffer = ByteBuffer.allocate(16);
        // 设置版本号、填充位、扩展位、CSRC计数
//        buffer.put((byte) 0x80); // 版本号(2 bits) + 填充位(1 bit) + 扩展位(1 bit) + CSRC计数(4 bits)
        buffer.put((byte) 0x90); // 版本号(2 bits) + 填充位(1 bit) + 扩展位(1 bit) + CSRC计数(4 bits)
        // 设置标记位、负载类型 08 媒体类型
//        buffer.put((byte) ((marker << 7) | payloadType)); // 标记位(1 bit) + 负载类型(7 bits)
        buffer.put((byte) 0x08); // 标记位(1 bit) + 负载类型(7 bits)
        // 设置序列号
        buffer.putShort((short) sequenceNumber);
        // 设置时间戳
        buffer.putInt((int) timestamp);
        // 设置同步源(SSRC)标识符
//        buffer.putInt(12345678);
        buffer.put((byte) 0x1D);
        buffer.put((byte) 0xEA);
        buffer.put((byte) 0xA4);
        buffer.put((byte) 0xC0);
        //e0 00 profile
        buffer.put((byte) 0xe0);
        buffer.put((byte) 0x00);
        //扩展头部
        buffer.put((byte) 0x00);
        buffer.put((byte) 0x08);
        // 返回RTP固定头部字节数组
        return buffer.array();
    }

    //信息对象头部
    public static byte[] createRTCHeaderInfo(int toId, int fromId, boolean isGroup, int v2, int seqNo, byte[] rtpHeader) {
        ByteBuffer buffer = ByteBuffer.allocate(32 + rtpHeader.length);
        // 设置RTP固定头部
        buffer.put(rtpHeader);
        buffer.putInt(toId);//被叫ID
        buffer.putInt(fromId);//主叫ID
        buffer.putInt(0);
        //1，2，3开头 4持续 5结束

        if (seqNo == 1) {
            if (isGroup) {
                buffer.putInt(1072);
                buffer.putInt(3840);
            } else {
                buffer.putInt(48);
                buffer.putInt(3840);
            }
            buffer.putInt(0);
            buffer.putInt(319356928);
            buffer.putInt(150994944);//0 Console (or other application) source
        } else if (seqNo == 2) {
            if (isGroup) {
                buffer.putInt(1040);
                buffer.putInt(3840);
            } else {
                buffer.putInt(16);
                buffer.putInt(3840);
            }
            buffer.putInt(0);
            buffer.putInt(318767106);
            buffer.putInt(0);//0 Console (or other application) source
        } else if (seqNo == 3) {
            if (isGroup) {
                buffer.putInt(5136);
                buffer.putInt(3840);
            } else {
                buffer.putInt(4112);
                buffer.putInt(3840);
            }

            buffer.putInt(0);
            buffer.putInt(319029250);
            buffer.putInt(0);//0 Console (or other application) source
        } else if (seqNo == 4) {
            //主要传输
            if (isGroup) {
                buffer.putInt(5120);
            } else {
                buffer.putInt(4096);
            }

            buffer.putInt(3840);
            buffer.putInt(0);
            buffer.putInt(318898178);
            buffer.putInt(0);//0 Console (or other application) source
        } else if (seqNo == 0) {
            if (isGroup) {
                buffer.putInt(1025);
            } else {
                buffer.putInt(1);
            }
            buffer.putInt(3840);
            buffer.putInt(0);
            buffer.putInt(318963714);
            buffer.putInt(0);//0 Console (or other application) source
        } else {
            //主要传输
            if (isGroup) {
                buffer.putInt(5120);
            } else {
                buffer.putInt(4096);
            }
            buffer.putInt(3840);
            buffer.putInt(0);
            buffer.putInt(318898178);
            buffer.putInt(0);//0 Console (or other application) source
        }


        // 返回RTC头部字节数组
        return buffer.array();
    }

    public static void main(String[] args) {
        byte[] n = createRTPHeader(44504, 0, 0, System.currentTimeMillis());
//        byte[] n2 = createRTCHeaderInfo(9001, 772, 0,5120,3840,0,318898180,0, n);
        System.out.println(ByteUtils.byteArrayToHexString(n));
//        System.out.println(ByteUtils.byteArrayToHexString(n2));
    }


    public static byte[] createRTCHeader(int version, int sampleRate, int channelCount, int frameSize, byte[] rtpHeader) {
        // 创建RTC头部字节缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(16 + rtpHeader.length);

        // 设置版本号
        buffer.put((byte) version);

        // 设置采样率
        buffer.putInt(sampleRate);

        // 设置声道数量
        buffer.putInt(channelCount);

        // 设置帧大小
        buffer.putInt(frameSize);

        // 设置RTP固定头部
        buffer.put(rtpHeader);

        // 返回RTC头部字节数组
        return buffer.array();
    }

    public void sendRTCHeaderAndRTPHeader(byte[] rtcHeader, byte[] rtpHeader, byte[] audioDate) {
        // 在这里实现发送RTC头部和RTP固定头部的逻辑
//        System.out.println("发送RTC头部：" + byteArrayToHexString(rtcHeader));
//        System.out.println("发送RTP固定头部：" + byteArrayToHexString(rtpHeader));
//        System.out.println("长度：" + rtcHeader.length);
//        byte[] rtp = {(byte) 0x90,//版本号
//                0x08,//媒体类型
//                0x54, 0x22,//序列号
//                0x00, 0x00, 0x0d, 0x20, 0x1d, (byte) 0xea, (byte) 0xa4, (byte) 0xc0,
//                (byte) 0xe0, 0x00,//profile
//                0x00, 0x08//扩展长度
//        };
        try {


            // 设置目标IP地址和端口号
            InetAddress destinationAddress = InetAddress.getByName(IP);
            int destinationPort = PORT; // 替换为实际的目标端口号

            // 创建数据报文
//            byte[] packetData1 = byteMerge(rtpHeader,rtcHeader);
//            byte[] packetData = byteMerge(packetData1, audioDate);
            byte[] packetData = byteMerge(rtcHeader, audioDate);
//            byte[] packetData = byteMerge(rtp, audioDate);

            System.out.println(ByteUtils.byteArrayToHexString(packetData)+"\n");
            DatagramPacket packet = new DatagramPacket(packetData, packetData.length, destinationAddress, destinationPort);

            // 发送数据报文
            socket.send(packet);
//            // 关闭Socket
//            socket.close();

//            System.out.println("已发送RTC头部和RTP固定头部");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 辅助方法：将字节数组转换为十六进制字符串
    public static String byteArrayToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString();
    }

    //合并
    public static byte[] byteMerge(byte[] bt1, byte[] bt2) {
        byte[] bt3 = new byte[bt1.length + bt2.length];
        System.arraycopy(bt1, 0, bt3, 0, bt1.length);
        System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);
        return bt3;
    }


    /**
     * 分段*
     *
     * @param data
     * @param chunkSize
     * @return
     */
    public static List<byte[]> splitByteArray(byte[] data, int chunkSize) {
        List<byte[]> chunks = new ArrayList<>();
        int offset = 0;
        while (offset < data.length) {
            int length = Math.min(chunkSize, data.length - offset);
            byte[] chunk = new byte[length];
            System.arraycopy(data, offset, chunk, 0, length);
            chunks.add(chunk);
            offset += chunkSize;
        }
        return chunks;
    }

    public String getIP() {
        return IP;
    }

    public void setIP(String IP) {
        this.IP = IP;
    }

    public int getPORT() {
        return PORT;
    }

    public void setPORT(int PORT) {
        this.PORT = PORT;
    }

    public int getAudioSendSta() {
        return audioSendSta;
    }

    public void setAudioSendSta(int audioSendSta) {
        this.audioSendSta = audioSendSta;
    }

    public Request getRequest() {
        return request;
    }

    public void setRequest(Request request) {
        this.request = request;
    }

    //    public static void main(String[] args) {
//
//        String content = "Lh$TuE@@+XXqqopSIP/2.0 200 OK\n" +
//                "Via: SIP/2.0/UDP 192.168.88.253:6001;received=192.168.88.253;branch=6cnrdmr0e76i\n" +
//                "Call-ID: 81009018998940879159@192.168.88.253\n" +
//                "From: <sip:772@192.168.88.253>;tag=u96lvip9vp\n" +
//                "To: <sip:9001@192.168.88.241>;tag=0uwvh4RRzCj1TUoIPEoZugOdPOxSoaf7\n" +
//                "CSeq: 1 INVITE\n" +
//                "Contact: <sip:9001@192.168.88.241:6001>\n" +
//                "Allow: SUBSCRIBE, NOTIFY\n" +
//                "Content-Type: application/sdp\n" +
//                "Content-Length:   217\n" +
//                "\n" +
//                "v=0\n" +
//                "o=EXCERA 0 0 IN IP4 192.168.88.241\n" +
//                "s=AIS-GATEWAY\n" +
//                "c=IN IP4 192.168.88.241\n" +
//                "t=0 0\n" +
//                "m=audio 50000 RTP/AVP 0 8 100\n" +
//                "a=rtpmap:0 PCMU/8000\n" +
//                "a=rtpmap:8 PCMA/8000\n" +
//                "a=rtpmap:100 DMR-AIS/8000\n" +
//                "a=mmf\n" +
//                "a=floor\n" +
//                "a=ptime:0\n";
//
//        String audioRtcVo = content.substring(content.indexOf("m=audio ")+7,content.indexOf(" RTP/AVP ")).replace(" ","");
//        System.out.println("INVITE发送端口："+audioRtcVo);
////        int max = 30000;
////        int min = 20000;
////        System.out.println(new Random().nextInt(max - min + 1) + min);
////        System.out.println(new Random().nextInt(max - min + 1) + min);
////        System.out.println(new Random().nextInt(max - min + 1) + min);
////        System.out.println(new Random().nextInt(max - min + 1) + min);
////        System.out.println(new Random().nextInt(max - min + 1) + min);
//    }

    private static byte[] convertPCMtoG711a(byte[] pcmData,AudioFormat pcmFormat) {
//        AudioFormat pcmFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 8000, 16, 1, 2, 8000, false);
        AudioFormat g711aFormat = new AudioFormat(AudioFormat.Encoding.ULAW, 8000, 8, 1, 1, 8000, false);

//        AudioFormat g711aFormat = new AudioFormat(8000, 8, 1, true, false);


        try (ByteArrayInputStream inputStream = new ByteArrayInputStream(pcmData);
             AudioInputStream audioInputStream = new AudioInputStream(inputStream, pcmFormat, pcmData.length / pcmFormat.getFrameSize());
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            AudioInputStream g711aStream = AudioSystem.getAudioInputStream(g711aFormat, audioInputStream);
            byte[] buffer = new byte[882];
            int bytesRead;

            while ((bytesRead = g711aStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }

            return outputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}
